import type { CdnSource, DependencyStatusReport } from './types'
import { logger, pageWindow } from '../../types/constants'
import { SYMBOLS } from '../../types/interfaces'
import { ToolKit } from '../utils/toolkit'
import {
  ExternalDependency,
  getBuiltInDependencyConfigs,
  getDependencyMeta,
  getDependencyResources,
  getEnabledDependencyConfigs,
  REQUIRE_DEPENDENCY_CONFIG,
} from './types'

/**
 * 依赖加载管理器
 */
export class DependencyManager {
  private readonly name: string = 'DependencyManager'
  // 单例实例
  private static instance: DependencyManager
  // 加载状态记录
  private loadedDependencies = new Map<ExternalDependency, boolean>()
  // 默认检查配置
  private static readonly DEFAULT_CHECK_CONFIG = {
    interval: 50, // 检查间隔(ms)
    timeout: 1000, // 超时时间(ms)
    maxAttempts: 10, // 最大尝试次数
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): DependencyManager {
    if (!DependencyManager.instance) {
      DependencyManager.instance = new DependencyManager()
    }
    return DependencyManager.instance
  }

  /**
   * 加载所有依赖项
   */
  async loadAll(): Promise<void> {
    if (this.isAllLoaded()) {
      logger.info(`[${this.name}] 所有依赖项已加载，跳过加载`)
      return
    }

    const total = Object.keys(ExternalDependency).length
    let current = 1

    for (const dep of Object.values(ExternalDependency)) {
      logger.important(`[${this.name}] (${current++}/${total}) 正在加载 ${dep}`)
      await this.load(dep)
    }
  }

  /**
   * 加载单个依赖
   */
  async load(dep: ExternalDependency): Promise<void> {
    if (this.isLoaded(dep)) {
      logger.info(`[${this.name}] ${dep} 已加载，跳过加载`)
      return
    }

    const config = REQUIRE_DEPENDENCY_CONFIG[dep]
    const isBuiltIn = config.builtIn
    let lastError: Error | null = null

    if (!config.enabled) {
      logger.info(`[${this.name}] ${dep} 未启用，跳过加载`)
      return
    }

    // 第一步：检查油猴是否已加载主源
    try {
      if (await this._checkPrimarySource(dep)) {
        if (isBuiltIn) {
          logger.info(`[${this.name}] ${dep} 内建依赖已加载`)
        }
        else {
          logger.info(`[${this.name}] ${dep} 外部依赖已加载`)
        }

        this.loadedDependencies.set(dep, true)
        return
      }
    }
    catch (err) {
      lastError = err as Error
      logger.warn(`[${this.name}] ${dep} 主源检查失败 - ${lastError.message}`)
    }

    // 第二步：尝试备用源（非油猴加载）
    const resources = isBuiltIn
      ? getDependencyResources(config)
      : getDependencyResources(config).filter(res => res.source !== config.meta.mainSource)
    const fallbackSources = resources.map(res => res.source)

    for (const source of fallbackSources) {
      try {
        await this._attemptLoad(dep, source)
        this.loadedDependencies.set(dep, true)
        logger.info(`[${this.name}] ${dep} 已通过备用源 ${source} 加载成功`)
        return
      }
      catch (err: any) {
        lastError = err
        logger.error(`[${this.name}] ${dep} 备用源 ${source} 加载失败: ${err.message}`)
      }
    }

    throw new Error(`${dep} 所有源加载失败: ${lastError?.message}`)
  }

  private async _checkPrimarySource(dep: ExternalDependency): Promise<boolean> {
    const config = REQUIRE_DEPENDENCY_CONFIG[dep]
    const { maxAttempts = 3, interval = 200 } = config.retryPolicy || {}
    let lastError: Error | null = null

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        if (await this._checkAvailable(dep)) {
          logger.important(`[${this.name}] ${dep} 主源验证成功 (尝试 ${attempt}/${maxAttempts})`)
          return true
        }
        throw new Error(`依赖未检测到`)
      }
      catch (err) {
        lastError = err as Error
        if (attempt < maxAttempts) {
          await new Promise(resolve => setTimeout(resolve, interval))
        }
      }
    }

    throw new Error(`${dep} 主源验证失败: ${lastError?.message}`)
  }

  /**
   * 尝试从指定源加载依赖
   */
  private async _attemptLoad(dep: ExternalDependency, source: CdnSource): Promise<void> {
    const resource = getDependencyResources(REQUIRE_DEPENDENCY_CONFIG[dep]).find(r => r.source === source)

    if (!resource)
      throw new Error(`未找到 ${dep} 的 ${source} 源配置`)

    await this._injectScript(resource.url, {
      integrity: resource.integrity,
      crossorigin: resource.crossorigin,
    })

    if (!await this._checkAvailable(dep)) {
      throw new Error(`${dep} 加载后不可用`)
    }
  }

  /**
   * 注入脚本
   */
  private _injectScript(src: string, attributes: {
    integrity?: string
    crossorigin?: string
  } = {}): Promise<void> {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = src
      script.async = true

      if (attributes.crossorigin)
        script.crossOrigin = attributes.crossorigin

      if (attributes.integrity)
        script.integrity = attributes.integrity

      // 添加追踪属性
      script.setAttribute('data-injected-by', this.name)

      script.onload = () => {
        script.setAttribute('data-loaded', 'true')
        resolve()
      }
      script.onerror = () => {
        script.setAttribute('data-error', 'true')
        reject(new Error(`脚本加载失败: ${src}`))
      }

      document.head.appendChild(script)
    })
  }

  /**
   * 检查依赖是否可用
   */
  private async _checkAvailable(dep: ExternalDependency): Promise<boolean> {
    const checker = this._getAvailabilityChecker(dep)
    const { interval, timeout, maxAttempts } = DependencyManager.DEFAULT_CHECK_CONFIG

    return new Promise((resolve) => {
      const startTime = Date.now()
      let attempts = 0

      const check = () => {
        attempts++

        if (checker) {
          resolve(true)
          return
        }

        const elapsed = Date.now() - startTime

        if (elapsed > timeout || attempts >= maxAttempts) {
          resolve(false)
          return
        }

        setTimeout(check, interval)
      }

      check()
    })
  }

  /**
   * 获取依赖可用性检查器
   */
  private _getAvailabilityChecker(dep: ExternalDependency): boolean {
    // 如果当前作用域没有，从 unsafeWindow 复制
    if (!window.echarts && pageWindow.echarts)
      window.echarts = pageWindow.echarts

    return ToolKit.environment.checkDependencyLoaded(dep)
  }

  /**
   * 检查单个依赖是否已加载
   */
  isLoaded(dep: ExternalDependency): boolean {
    return this.loadedDependencies.get(dep) ?? false
  }

  /**
   * 检查所有依赖是否已加载
   */
  isAllLoaded(): boolean {
    return Object.values(ExternalDependency)
      .every(dep => this.isLoaded(dep))
  }

  /**
   * 挂载单个依赖到全局
   */
  mountToWindow(dep: ExternalDependency): void {
    const config = REQUIRE_DEPENDENCY_CONFIG[dep]
    const { windowProperty } = config

    // 检查依赖是否已加载
    if (config.checkMounted && !this.isLoaded(dep)) {
      logger.warn(`[${this.name}] ${dep} 未加载，无法挂载到全局`)
      return
    }

    // 根据依赖类型设置全局变量
    switch (dep) {
      case ExternalDependency.JQuery:
        ToolKit.environment.setJqueryToWindow()
        logger.info(`[${this.name}] ${dep} 已挂载到全局 (window.$, window.jQuery)`)
        break

      default:
        // 通用挂载逻辑
        if (windowProperty && !(window as any)[windowProperty]) {
          const existingValue = (window as any)[dep] || (window as any)[windowProperty.toLowerCase()]
          if (existingValue) {
            (window as any)[windowProperty] = existingValue
            logger.info(`[${this.name}] ${dep} 已挂载到全局 (window.${windowProperty})`)
          }
          else {
            logger.warn(`[${this.name}] ${dep} 未找到可挂载的值`)
          }
        }
        else {
          logger.info(`[${this.name}] ${dep} 已在全局存在 (window.${windowProperty})`)
        }
    }
  }

  /**
   * 挂载所有已加载的依赖到全局
   */
  mountAllToWindow(): void {
    const mountTargets = getBuiltInDependencyConfigs()
      .map(([dep]) => dep)

    if (mountTargets.length === 0) {
      logger.info(`[${this.name}] 没有可挂载的依赖`)
      return
    }

    logger.important(`[${this.name}] 开始挂载所有依赖到全局（涉及${mountTargets.length}个）`)

    mountTargets.forEach((dep) => {
      const config = REQUIRE_DEPENDENCY_CONFIG[dep]

      if (!config.checkMounted || (config.checkMounted && this.isLoaded(dep))) {
        this.mountToWindow(dep)
      }
      else {
        logger.warn(`[${this.name}] ${dep} 未加载，跳过挂载`)
      }
    })

    logger.info(`[${this.name}] 所有依赖挂载完成`)
  }

  /**
   * 获取详细的依赖状态报告
   * 返回包含加载状态、版本信息等的完整报告
   */
  getDependencyStatusReport(): DependencyStatusReport[] {
    return getEnabledDependencyConfigs(true).map(([dep]) => {
      const config = REQUIRE_DEPENDENCY_CONFIG[dep]
      const meta = getDependencyMeta(dep)
      const isLoaded = this.isLoaded(dep)

      let actualVersion = meta.version
      let versionSource: 'config' | 'runtime' = 'config'

      try {
        switch (dep) {
          case ExternalDependency.JQuery:
            if (isLoaded && window.$ && window.$.fn) {
              actualVersion = window.$.fn.jquery || actualVersion
              versionSource = 'runtime'
            }
            break

          default:
            if (isLoaded && window[config.windowProperty!]) {
              actualVersion = window[config.windowProperty!].version || actualVersion
              versionSource = 'runtime'
            }
            break
        }
      }
      // eslint-disable-next-line unused-imports/no-unused-vars
      catch (error: any) {
        // 忽略版本获取错误
      }

      return {
        dependency: dep,
        enabled: config.enabled,
        builtIn: config.builtIn,
        loaded: isLoaded,
        configuredVersion: meta.version,
        actualVersion,
        versionSource,
        windowProperty: config.windowProperty,
        meta,
      }
    })
  }

  /**
   * 打印详细的依赖状态报告
   * 以表格形式显示所有依赖的详细信息
   */
  printDetailedReport(): void {
    const report = this.getDependencyStatusReport()

    logger.important(`[${this.name}] ${SYMBOLS.UI.CHART} 详细依赖状态报告`)
    logger.info('='.repeat(80))

    report.forEach((item) => {
      const statusIcon = item.enabled && item.loaded ? `${SYMBOLS.COLOR_CIRCLES.GREEN}` : `${SYMBOLS.COLOR_CIRCLES.WHITE}`
      const versionSource = item.versionSource === 'runtime' ? '(运行时)' : '(配置)'

      logger.attention(`${statusIcon} ${item.dependency.padEnd(12)} v${item.actualVersion} ${versionSource}`)
      logger.info(`启用: ${item.enabled ? '是' : '否'} | 内置: ${item.builtIn ? '是' : '否'} | 状态: ${item.loaded ? '已加载' : '未加载'}`)
      if (item.windowProperty) {
        logger.info(`全局属性: window.${item.windowProperty}`)
      }
    })

    const loadedCount = report.filter(item => item.loaded).length
    const enabledCount = report.filter(item => item.enabled).length

    logger.info(`加载状态: ${loadedCount}/${enabledCount}`)
    logger.info('='.repeat(80))
  }
}
